package io.github.luons.mediator.generator.model;

import io.github.luons.docs.node.ParamNode;
import io.github.luons.docs.node.RequestNode;
import io.github.luons.mediator.core.utils.JacksonUtils;
import io.github.luons.mediator.generator.entity.ParamNodeBean;
import io.github.luons.mediator.generator.entity.YmlConstant;
import io.github.luons.mediator.generator.utils.YmlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * RequestYml
 *
 * @author : luons
 */
@Slf4j
public class RequestYml {

    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)}");

    public static LinkedHashMap<String, Object> genMethodRequest(String filePath, RequestNode request) {
        LinkedHashMap<String, Object> methodMap = new LinkedHashMap<>();
        if (Objects.isNull(request) || StringUtils.isEmpty(request.getUrl())) {
            return methodMap;
        }
        String operatorId = request.getMethodName().trim();
        checkUrlParameters(request.getUrl());
        if (YmlConstant.CONTROLLER_METHOD_LIST.contains(operatorId)) {
            log.error("=============== controller method = {} is reduplicate please check！", operatorId);
        }
        YmlConstant.CONTROLLER_METHOD_LIST.add(operatorId);

        // TODO 默认GET请求，暂时只设置1种请求方式
        List<String> methodList = request.getMethod();
        String method = CollectionUtils.isEmpty(methodList) ? "get" : methodList.get(0).toLowerCase();

        // 生成Request和Response
        LinkedHashMap<String, Object> parameters = new LinkedHashMap<>();
        parameters.put("description", request.getDescription());
        parameters.put("operationId", operatorId);
        parameters.put("x-openapi-internal", false);
        List<Map<String, Object>> paramNodesList = new ArrayList<>();
        parameters.put("parameters", paramNodesList);

        parameters.put("responses", ResponseYml.genResponse(filePath, operatorId, request.getResponseNode()));
        methodMap.put(method, parameters);

        List<ParamNode> paramNodes = request.getParamNodes();
        if (CollectionUtils.isEmpty(paramNodes)) {
            return methodMap;
        }
        // 获取Request参数体
        String filename = (operatorId + "Dto");
        Map<String, Object> requestDtoMap = new LinkedHashMap<>();
        for (ParamNode pNode : paramNodes) {
            if (Objects.isNull(pNode)) {
                continue;
            }
            if (YmlUtils.URL_PARAM_MAP.containsKey(pNode.getName())) {
                YmlUtils.URL_PARAM_MAP.put(pNode.getName(), ParamNodeBean.builder().name(pNode.getName())
                        .type(YmlUtils.transformType(pNode.getType()))
                        .description(pNode.getDescription()).jsonBody(false).build());
                continue;
            }
            boolean isObject = "_object".equalsIgnoreCase(pNode.getType());
            Map<String, Object> param = genRequestObjectMap(pNode, isObject, methodList.size() == 2);
            paramNodesList.add(param);
            if (!pNode.getJsonBody() || !isObject) {
                continue;
            }
            // TODO PostJson形式
            param.put("in", "body");
            LinkedHashMap<String, Object> schema = new LinkedHashMap<>();
            param.put("schema", schema);
            String title = StringUtils.capitalize(pNode.getName());
            // schema.put("$ref", MessageFormat.format(YmlConstant.BASE_PATH, filename, title));
            schema.put("type", "object");
            Map<String, Object> paramMap = JacksonUtils.parseAsMapKeyString(pNode.getDescription());
            Map<String, Object> dtoMap = YmlUtils.generateDtoMap((filename + "Model"), paramMap);

            schema.put("properties", dtoMap);
            // 判断是否有深层的 Bean
            if (paramMap.entrySet().stream().anyMatch(entry -> entry.getValue() instanceof Map)) {
                requestDtoMap.putAll(YmlUtils.generateModelMap(dtoMap, title, request.getDescription(), false));
            }
        }
        if (CollectionUtils.isEmpty(paramNodesList)) {
            parameters.remove("parameters");
        }
        // 生成 Yml 文件 Dto
        YmlUtils.ymlModel(filePath, filename, requestDtoMap);
        // 生成 Yml Dto 文件依赖的Model
        if (MapUtils.isNotEmpty(YmlUtils.DTO_MODEL_MAP)) {
            for (Map.Entry<String, Object> modelEntry : YmlUtils.DTO_MODEL_MAP.entrySet()) {
                if (Objects.isNull(modelEntry.getValue()) || !(modelEntry.getValue() instanceof Map)) {
                    continue;
                }
                YmlUtils.ymlModel(filePath, modelEntry.getKey(), (Map<String, Object>) modelEntry.getValue());
            }
        }
        YmlUtils.DTO_MODEL_MAP.clear();
        return methodMap;
    }

    private static void checkUrlParameters(String url) {
        Matcher matcher = PATTERN.matcher(url);
        while (matcher.find()) {
            String groupStr = matcher.group(1);
            if (YmlUtils.URL_PARAM_MAP.containsKey(groupStr)) {
                continue;
            }
            YmlUtils.URL_PARAM_MAP.put(groupStr, ParamNodeBean.builder().name(groupStr).jsonBody(false).build());
        }
    }

    private static LinkedHashMap<String, Object> genRequestObjectMap(ParamNode node, boolean isObject, boolean filterField) {

        LinkedHashMap<String, Object> value = new LinkedHashMap<>();
        value.put("name", node.getName());
        value.put("required", Objects.nonNull(node.getRequired()) && node.getRequired());
        // value.put("required", node.getRequired());
        value.put("in", "query");
        if (!isObject) {
            value.put("type", YmlUtils.transformType(node.getType()));
        }
        // TODO update filters fields
        if (filterField && "filters".equals(node.getName())) {
            value.put("type", "array");
            LinkedHashMap<String, Object> refMap = new LinkedHashMap<>();
            refMap.put("$ref", "../../common/model/Filter.yaml#/definitions/filter");
            value.put("items", refMap);
        }
        value.put("description", node.getJsonBody() ? "" : node.getDescription());
        return value;
    }

}
